#ifndef _MY_STACK_TCC_
#define _MY_STACK_TCC_

template<typename T>
void my_stack<T>::push(const T& data) {
	countedNodePtr newNode;
	newNode.ptr = new node(data);
	newNode.externalCount = 1;
	newNode.ptr->next = head.load();
	while (!head.compare_exchange_weak(newNode.ptr->next, newNode,
			memory_order_release, memory_order_relaxed));
}

template<typename T>
shared_ptr<T> my_stack<T>::pop() {
	countedNodePtr oldHead = head.load(memory_order_relaxed);
	for (;;) {
		increaseHeadCount(oldHead);
		node* const ptr = oldHead.ptr;
		if (!ptr) {
			/** Si c'est le dernier élément de la pile, on retourne
			 * simplement un pointeur vide */
			return shared_ptr<T>();
		}
		/** Il faut chercher à synchroniser fetch_add et fetch_sub */
		if (head.compare_exchange_strong(oldHead, ptr->next,
			memory_order_relaxed, memory_order_relaxed)) {
			shared_ptr<T> result;
			result.swap(ptr->data);
			const int k = oldHead.externalCount - 2;
			if (ptr->internalCount.fetch_add(k, memory_order_relaxed) == -k) {
				delete ptr;
			}
			return result;
		} else if (ptr->internalCount.fetch_sub(1, memory_order_relaxed) == 1) {
			ptr->internalCount.load(memory_order_acquire);
			delete ptr;
		}
	}
}

template<typename T>
void my_stack<T>::increaseHeadCount(countedNodePtr& oldCounter) {
	countedNodePtr newCounter;
	do {
		newCounter = oldCounter;
		++newCounter.externalCount;
	} while (!head.compare_exchange_strong(oldCounter, newCounter,
	memory_order_acquire, memory_order_relaxed));
	oldCounter.externalCount = newCounter.externalCount;
}

template<typename T>
my_stack<T>::~my_stack() {
	while (pop());
}

#endif
